package cn.wolfcode.luowowo.search.service.impl;

import cn.wolfcode.luowowo.search.query.StrategySearchQuery;
import cn.wolfcode.luowowo.search.repository.StrategyTemplateSearchRepository;
import cn.wolfcode.luowowo.search.service.StrategyTemplateSearchService;
import cn.wolfcode.luowowo.search.template.StrategyTemplate;
import cn.wolfcode.luowowo.search.vo.StatisVo;
import com.alibaba.dubbo.config.annotation.Service;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.bucket.composite.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;

import java.util.*;

/**
 * @author win
 */
@Service
public class StrategyTemplateSearchServiceImpl implements StrategyTemplateSearchService {

    public static final String MAP_KEY_THEME = "theme";
    public static final String MAP_KEY_DESTS = "dests";

    @Autowired
    private ElasticsearchTemplate template;

    @Autowired
    private StrategyTemplateSearchRepository repository;


    @Override
    public void saveOrUpdate(StrategyTemplate strategy) {
        repository.save(strategy);
    }

    @Override
    public List<Map<String, Object>> queryThemeRankTop10() {
        List<Map<String, Object>> list = new ArrayList<>();
        List<StatisVo> themes = getThemes("groupByTheme", "themeId", "themeName");
        Map<String, Object> map = null;
        for (StatisVo vo : themes) {
            List<StatisVo> voList = getDestinationsByThemeId(vo.getId());
            map = new HashMap<>(2);
            map.put(MAP_KEY_THEME, vo);
            map.put(MAP_KEY_DESTS, voList);
            list.add(map);
        }
        // 取前十个主题
        return list.size() < 10 ? list : list.subList(0, 9);
    }

    private List<StatisVo> getThemes(String groupByName, String fieldId, String fieldName) {
        return getThemes(groupByName, fieldId, fieldName, null);
    }

    private List<StatisVo> getThemes(String groupByName, String fieldId, String fieldName, QueryBuilder query) {
        List<CompositeValuesSourceBuilder<?>> sources = new ArrayList<>();
        CompositeAggregationBuilder aggregationBuilder = new CompositeAggregationBuilder(groupByName, sources);
        TermsValuesSourceBuilder themeId = new TermsValuesSourceBuilder("id");
        themeId.field(fieldId);
        TermsValuesSourceBuilder themeName = new TermsValuesSourceBuilder("name");
        themeName.field(fieldName);
        sources.add(themeId);
        sources.add(themeName);

        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.addAggregation(aggregationBuilder);
        if (query != null) {
            builder.withQuery(query);
        }
        builder.withPageable(PageRequest.of(0, 1));

        AggregatedPage<StrategyTemplate> aggregatedPage = (AggregatedPage<StrategyTemplate>) repository.search(builder.build());
        InternalComposite internalComposite = (InternalComposite) aggregatedPage.getAggregation(groupByName);
        List<StatisVo> list = new ArrayList<>();
        for (CompositeAggregation.Bucket bucket : internalComposite.getBuckets()) {
            Long id = (Long) bucket.getKey().get("id");
            String name = (String) bucket.getKey().get("name");
            long count = bucket.getDocCount();
            StatisVo statisVo = new StatisVo(id, name, count);
            list.add(statisVo);
        }
        // 排序
        Collections.sort(list, (o1, o2) -> Long.valueOf((o2.getCount() - o1.getCount())).intValue());
        return list;
    }

    @Override
    public List<StatisVo> queryThemes() {
        return getThemes("groupByTheme", "themeId", "themeName");
    }

    @Override
    public List<StatisVo> queryAbroadsThemes() {
        BoolQueryBuilder countryId = QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery("countryId", 1));
        return getThemes("groupByCountry", "countryId", "countryName", countryId);
    }

    @Override
    public List<StatisVo> queryChinasThemes() {
        return getThemes("groupByProvinceId", "provinceId", "provinceName");
    }

    @Override
    public Page<StrategyTemplate> query(StrategySearchQuery qo) {
        QueryBuilder builder = null;
        int type = qo.getType();
        if (type == StrategySearchQuery.CONDITION_TYPE_CHINA) {
            // 国内
            builder = QueryBuilders.termQuery("provinceId", qo.getTypeValue());
        } else if (type == StrategySearchQuery.CONDITION_TYPE_ABROAD) {
            // 国外
            builder = QueryBuilders.termQuery("countryId", qo.getTypeValue());
        } else if (qo.getType() == StrategySearchQuery.CONDITION_TYPE_THEME_ID) {
            // 标题
            builder = QueryBuilders.termQuery("themeId", qo.getTypeValue());
        }
        PageRequest page = PageRequest.of(qo.getCurrentPage() - 1, qo.getPageSize(), Sort.Direction.DESC, qo.getOrderBy());
        return builder == null ? repository.findAll(page) : repository.search(builder, page);
    }

    @Override
    public List<StrategyTemplate> queryByDestName(String destName) {
        return repository.findByDestName(destName);
    }

    /**
     * 根据主题编号获取目的地信息
     *
     * @param themeId 主题编号
     * @return 返回目的地集合
     */
    private List<StatisVo> getDestinationsByThemeId(Long themeId) {
        List<StrategyTemplate> templates = repository.findByThemeId(themeId);
        List<StatisVo> list = new ArrayList<>();
        List<String> tempList = new ArrayList<>();
        // 去除重复的目的地
        for (StrategyTemplate strategyTemplate : templates) {
            String destName = strategyTemplate.getDestName();
            if (!tempList.contains(destName)) {
                list.add(new StatisVo(strategyTemplate.getDestId(), strategyTemplate.getDestName()));
                tempList.add(destName);
            }
        }
        return list;
    }
}
